﻿namespace Jhong.Data.Core.Infrastructure
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    using System.Reflection.Emit;

    internal class ColumnInfo<T>
    {
        public PropertyInfo Property { get; set; }

        public string ColumnName { get; set; }

        public bool Key { get; set; }

        public bool Autoincrement { get; set; }

        public Func<T, object> GetterDelegate { get; set; }
    }

    internal partial class EntityContext<T>
    {
        public static string TableName { get; private set; }

        public static IList<ColumnInfo<T>> Columns { get; private set; }

        public static Type Type { get; private set; }

        private static object _lock = new object();

        static EntityContext()
        {
            Type = typeof(T);
            TableName = GetTableName();
            Columns = GetColumns();
        }

        private static string GetTableName()
        {
            var attributes = Type.GetCustomAttributes(false);
            var attribute = attributes.OfType<TableAttribute>().FirstOrDefault();
            if (null != attributes) return attribute.TableName;
            return Type.Name;
        }

        private static IList<ColumnInfo<T>> GetColumns()
        {
            var columns = new List<ColumnInfo<T>>();
            var properties = Type.GetProperties();
            foreach (var p in properties)
            {
                if (null == p.GetGetMethod() || null == p.GetSetMethod()) continue;
                var ci = new ColumnInfo<T>() { Property = p };
                var attribute = p.GetCustomAttributes().OfType<ColumnAttribute>().FirstOrDefault();
                if (null == attribute)
                {
                    ci.Autoincrement = false;
                    ci.ColumnName = p.Name;
                    ci.Key = false;
                }
                else
                {
                    ci.ColumnName = attribute.ColumnName;
                    ci.Autoincrement = attribute.AutoIncrement;
                    ci.Key = attribute.Key;
                }
                ci.GetterDelegate = CreateGetterDelegate(p);
                columns.Add(ci);
            }
            return columns;
        }

        private static Func<T, object> CreateGetterDelegate(PropertyInfo info)
        {
            var dynamicMethod = new DynamicMethod(Guid.NewGuid().ToString("N") + "_GetterDelegate", typeof(object), new[] { typeof(T) });
            var il = dynamicMethod.GetILGenerator();
            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Callvirt, info.GetGetMethod());
            if (info.PropertyType.IsValueType) il.Emit(OpCodes.Box, info.PropertyType);
            il.Emit(OpCodes.Ret);
            return (Func<T, object>)dynamicMethod.CreateDelegate(typeof(Func<T, object>));
        }
    }
}